package com.momo.elasticsearch;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount;
import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by MOMO on 2019/3/11.
 * https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-supported-apis.html
 * https://www.elastic.co/guide/en/elasticsearch/reference/6.6/index.html
 * Index---》关系型数据库中的数据库（DataBase）
 * Type---》一个数据库下面的表
 * Document---》行
 * Field---》属性
 * 在一个关系型数据库里面，schema定义了表、每个表的字段，还有表和字段之间的关系。
 * 与之对应的，在ES中：Mapping定义索引下的Type的字段处理规则，即索引如何建立、索引类型、
 * 是否保存原始索引JSON文档、是否压缩原始JSON文档、是否需要分词处理、如何进行分词处理等。
 */
@Component
@Slf4j
public class ElasticsearchUtil {
    @Autowired
    private RestHighLevelClient client;

    //###################################   索引相关设置  ################################################

    /**
     * 创建索引
     *
     * @param createIndexReq
     * @param properties     结构: {name:{type:text}} {age:{type:integer}}
     * @return
     * @throws IOException
     */
    public boolean createIndex(CreateIndexReq createIndexReq, Map<String, Object> properties) {
        try {
            log.info(JSONObject.toJSONString(properties));
            CreateIndexRequest request = new CreateIndexRequest(createIndexReq.getIndex());
            request.settings(Settings.builder().put("index.number_of_shards", createIndexReq.getNumberOfShards())
                    .put("index.number_of_replicas", createIndexReq.getNumberOfReplicas()));
            Map<String, Object> jsonMap = new HashMap<>();
            Map<String, Object> mapping = new HashMap<>();
            mapping.put("properties", properties);
            jsonMap.put(createIndexReq.getIndexType(), mapping);
            request.mapping(createIndexReq.getIndexType(), jsonMap);
            //TODO 索引别名
//            request.alias(new Alias(createIndexReq.getAliasIndex()).filter(QueryBuilders.termQuery("user", "kimchy")));
            request.alias(new Alias(createIndexReq.getAliasIndex()));
            CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
            if (createIndexResponse.isAcknowledged()) {
                log.info("创建索引成功");
            } else {
                log.info("创建索引失败");
            }
            return createIndexResponse.isAcknowledged();
        } catch (ElasticsearchException e) {
            log.error("创建索引异常，索引名为：{}", e.getMessage());
        } catch (IOException e) {
            log.error("创建索引异常，索引名为：{}", e.getMessage());
        }
        return false;
    }

    /**
     * 删除索引
     *
     * @param indexDeleteReq
     * @return
     * @throws Exception
     */
    public boolean indexDelete(IndexDeleteReq indexDeleteReq) throws Exception {
        try {
            DeleteIndexRequest request = new DeleteIndexRequest(indexDeleteReq.getIndex());
            AcknowledgedResponse deleteIndexResponse = client.indices().delete(
                    request, RequestOptions.DEFAULT);
            log.info("删除索引相应 deleteResponse:{}", JSONObject.toJSONString(deleteIndexResponse));
            if (deleteIndexResponse.isAcknowledged()) {
                return true;
            }
        } catch (ElasticsearchException exception) {
            //没有找到索引
            if (exception.status() == RestStatus.NOT_FOUND) {

            }
        }

        return false;
    }

    /**
     * 查看索引
     *
     * @param indexDeleteReq
     * @return
     */
    public Object getIndex(IndexDeleteReq indexDeleteReq) {

        try {
            GetIndexRequest request = new GetIndexRequest().indices(indexDeleteReq.getIndex());
            GetIndexResponse getIndexResponse = client.indices().get(request, RequestOptions.DEFAULT);
            log.info("getIndexResponse 得到响应:{}", JSONObject.toJSONString(getIndexResponse));

            ImmutableOpenMap<String, MappingMetaData> indexMappings = getIndexResponse.getMappings().get(indexDeleteReq.getIndex());//索引
            log.info("索引 indexMappings:{}", JSONObject.toJSONString(indexMappings));

            Map<String, Object> indexTypeMappings = indexMappings.get(indexDeleteReq.getType()).getSourceAsMap();//文档类型
            log.info("文档类型 indexTypeMappings:{}", JSONObject.toJSONString(indexTypeMappings));

            List<AliasMetaData> indexAliases = getIndexResponse.getAliases().get(indexDeleteReq.getIndex());//获取索引别名列表
            log.info("获取索引别名列表 indexAliases:{}", JSONObject.toJSONString(indexAliases));

            String numberOfShardsString = getIndexResponse.getSetting(indexDeleteReq.getIndex(), "index.number_of_shards");
            log.info("分片个数 numberOfShardsString:{}", JSONObject.toJSONString(indexAliases));
            Settings indexSettings = getIndexResponse.getSettings().get(indexDeleteReq.getIndex());//检索索引的所有设置。
            Integer numberOfShards = indexSettings.getAsInt("index.number_of_shards", null);
//            TimeValue time = getIndexResponse.defaultSettings().get(indexDeleteReq.getIndex())
//                    .getAsTime("index.refresh_interval", null);
            return indexMappings;
        } catch (IOException e) {

        }
        return "";
    }

    /**
     * 为索引设置别名
     *
     * @param createIndexReq
     */
    public boolean indexAliases(CreateIndexReq createIndexReq) {
        try {
            IndicesAliasesRequest request = new IndicesAliasesRequest();
            IndicesAliasesRequest.AliasActions aliasAction =
                    new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                            .index(createIndexReq.getIndex())
                            .alias(createIndexReq.getAliasIndex());//为索引设置一个别名
            request.addAliasAction(aliasAction);
            AcknowledgedResponse indicesAliasesResponse =
                    client.indices().updateAliases(request, RequestOptions.DEFAULT);
            boolean acknowledged = indicesAliasesResponse.isAcknowledged();
            if (acknowledged) {
                return true;
            }
        } catch (IOException e) {
            log.error("为索引设置别名 异常:{}", e.getMessage());
        }
        return false;
    }

    /**
     * 判断索引是否都存在
     *
     * @param index
     * @return
     * @throws IOException
     */
    public boolean createIndexExists(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest();
        request.indices(index);
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        log.info("判断索引是否都存在: " + exists);
        return exists;
    }
    //###################################   映射（Mapping）相关设置  ################################################

    /**
     * 映射（Mapping）：相当于数据库中的schema，用来约束字段的类型，
     * 不过 Elasticsearch 的 mapping 可以不显示地指定、自动根据文档数据创建。
     * -----》》相当于建表语句
     *
     * @param createIndexReq
     * @param properties
     */
    public boolean putMapping(CreateIndexReq createIndexReq, Map<String, Object> properties) {
        try {
            PutMappingRequest request = new PutMappingRequest(createIndexReq.getIndex());
            request.type(createIndexReq.getIndexType());
            Map<String, Object> jsonMap = new HashMap<>();
            Map<String, Object> message = new HashMap<>();
            /*message.put("type", "text");
            properties.put("message", message);*/
            jsonMap.put("properties", properties);
            request.source(jsonMap);
            AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT);
            boolean acknowledged = putMappingResponse.isAcknowledged();
            return acknowledged;
        } catch (IOException e) {
            log.error("映射（Mapping）异常:{}", e.getMessage());
        }
        return false;
    }

    /**
     * 获取 建表语句
     *
     * @param createIndexReq
     * @return
     */
    public Map<String, Object> getMapping(CreateIndexReq createIndexReq) {
        try {
            GetMappingsRequest request = new GetMappingsRequest();
            request.indices(createIndexReq.getIndex());
            request.types(createIndexReq.getIndexType());
            GetMappingsResponse getMappingResponse = client.indices().getMapping(request, RequestOptions.DEFAULT);
            ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> allMappings = getMappingResponse.mappings();
            MappingMetaData typeMapping = allMappings.get(createIndexReq.getIndex()).get(createIndexReq.getIndexType());
            Map<String, Object> mapping = typeMapping.sourceAsMap();
            return mapping;
        } catch (IOException e) {
            log.error("获取（Mapping）异常:{}", e.getMessage());
        }
        return Maps.newHashMap();
    }

    //###################################   document 查询 相关设置  ################################################

    /**
     * 判断记录是否都存在
     *
     * @param index
     * @param type
     * @param id
     * @return
     * @throws IOException
     */
    public boolean existsDocument(String index, String type, String id) throws IOException {
        GetRequest getRequest = new GetRequest(index, type, id);
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");//禁用抓取存储字段
        boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
        log.info("exists: " + exists);
        return exists;
    }

    /**
     * 增加记录
     *
     * @param index
     * @param type
     * @param id
     * @param object
     * @throws IOException
     */
    public String addOneDocument(String index, String type, String id, Object object) {
        try {
            IndexRequest indexRequest = new IndexRequest(index, type, id);
            indexRequest.source(JSONObject.toJSONString(object), XContentType.JSON);
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            log.info("增加记录: {}", JSON.toJSONString(indexResponse));
            return indexResponse.getId();
        } catch (IOException e) {
            log.error("增加记录 异常:{}", e.getMessage());
        }
        return "";
    }


    /**
     * 获取记录信息
     *
     * @param index
     * @param type
     * @param id
     * @throws IOException
     */
    public void getOneDocument(String index, String type, Long id) {
        try {
            GetRequest getRequest = new GetRequest(index, type, id.toString());
            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
            log.info("get获取记录信息: {}", JSON.toJSONString(getResponse));
        } catch (IOException e) {
            log.error("获取记录信息 异常:{}", e.getMessage());
        }
//        return "";
    }

    /**
     * 更新记录信息
     *
     * @param index
     * @param type
     * @param id
     * @param obj
     * @throws IOException
     */
    public void updateOneDocument(String index, String type, String id, Object obj) throws IOException {
        UpdateRequest request = new UpdateRequest(index, type, id);
        request.doc(JSON.toJSONString(obj), XContentType.JSON);
        UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
        log.info("update: " + JSON.toJSONString(updateResponse));
    }

    /**
     * 删除记录
     *
     * @param index
     * @param type
     * @param id
     * @throws IOException
     */
    public void deleteOneDocument(String index, String type, String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(index, type, id);
        DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
        log.info("delete: " + JSON.toJSONString(response));
    }

    public void testSearchByDeviceNos() {

        try {
            //索引别名
            SearchRequest searchRequest = new SearchRequest("sys_core_alias");
            //大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求体的所有内容提供setter。
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            List<String> list = Lists.newArrayList();
            list.add("0869858030022348");
            list.add("0869858030024948");
            list.add("0869858030023379");
            list.add("0869858030024948");
            list.add("0869858030022348");
            list.add("0869858030024823");
            list.add("0869858030023767");
            list.add("0869858030024948");
            list.add("0869858030024153");
            list.add("0869858030023379");
            list.add("0869858030023783");
            searchSourceBuilder.query(QueryBuilders.termsQuery("deviceNo", list));
            searchRequest.source(searchSourceBuilder);

            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            log.info("耗时：{}", response.getTook());
            SearchHits hits = response.getHits();
            log.info("总数为：{}", hits.getTotalHits());
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit hit : searchHits) {
                log.info("search -> " + hit.getSourceAsString());
            }
        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
    }

    //精确查找
    public void termQuery() {
        try {
            BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
            //索引别名
            SearchRequest searchRequest = new SearchRequest("sys_core_alias");
            //大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求体的所有内容提供setter。
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //精确查找
            boolBuilder.must(QueryBuilders.matchQuery("deviceNo", "0869858030023379"));
            boolBuilder.must(QueryBuilders.matchQuery("longid", "302882617529864192"));
            sourceBuilder.query(boolBuilder);
            sourceBuilder.from(0);
            sourceBuilder.size(50);
            //搜索超时时间
            sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            searchRequest.source(sourceBuilder);
            //需要查询出的字段
            String[] includeFields = new String[]{"*"};
            //排除哪些字段
            String[] excludeFields = new String[]{"_type"};
            sourceBuilder.fetchSource(includeFields, excludeFields);

            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            log.info("耗时：{}", response.getTook());
            SearchHits hits = response.getHits();
            log.info("总数为：{}", hits.getTotalHits());
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit hit : searchHits) {
                log.info("search -> " + hit.getSourceAsString());
            }
        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
    }

    //分组查找 数量 查询（group by A, B ,...）
    public void aggregation() {
        try {
            //索引别名
            SearchRequest searchRequest = new SearchRequest("sys_core_alias");
            //通过首先创建适当的AggregationBuilder，然后将其设置在SearchSourceBuilder上，可以将聚合添加到搜索中。
            // 在下面的例子中，我们创建了一个基于公司名称的术语聚合，其中包含一个关于公司员工平均年龄的子聚合:
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //以 createTime 分组查询 数量
            //要计算每个球队每个位置的球员数，
            //select deptid, birthplace, count(*) as hasPeople_count from employee group by longid, createTime
            TermsAggregationBuilder aggregation = AggregationBuilders.terms("hasPeople_count").field("longid");
            TermsAggregationBuilder aAggregationBuilder2 = AggregationBuilders.terms("region_count").field("createTime");
            searchSourceBuilder.aggregation(aggregation.subAggregation(aAggregationBuilder2).size(20));
            //排序
            searchSourceBuilder.sort(new FieldSortBuilder("longid").order(SortOrder.DESC));
            searchRequest.source(searchSourceBuilder);
            log.info("searchSourceBuilder==", searchSourceBuilder.toString());
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            log.info("耗时：{}", searchResponse.getTook());
            Map<String, Aggregation> aggMap = searchResponse.getAggregations().asMap();

            aggMap.forEach((k, v) -> {
                Terms teamAgg = (Terms) aggMap.get("hasPeople_count");
                teamAgg.getBuckets().iterator();
                for (Terms.Bucket bucket : teamAgg.getBuckets()) {
                    System.out.println("key=" + bucket.getKey() + ";数量=" + bucket.getDocCount());
                    Terms terms2 = bucket.getAggregations().get("region_count");
                    for (Terms.Bucket bucket2 : terms2.getBuckets()) {
                        System.out.println("籍贯=" + bucket2.getKey() + ";员工数=" + bucket2.getDocCount());
                    }
                }
            });

        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
    }

    //分组查找 max/min/sum/avg 查询（group by A, B ,...）
    public void count() {
        try {
            //索引别名
            SearchRequest searchRequest = new SearchRequest("sys_core_alias");
            //通过首先创建适当的AggregationBuilder，然后将其设置在SearchSourceBuilder上，可以将聚合添加到搜索中。
            // 在下面的例子中，我们创建了一个基于公司名称的术语聚合，其中包含一个关于公司员工平均年龄的子聚合:
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //以 createTime 分组查询 数量
            //计算每个部门最高的工资
            //select deptid, max(salary) as max_salary from employee group by deptid
            ValueCountAggregationBuilder sumAggregationBuilder = AggregationBuilders.count("countcreateTime").field("atPowerPct");
            searchSourceBuilder.aggregation(sumAggregationBuilder);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            log.info("耗时：{}", searchResponse.getTook());
            Map<String, Aggregation> aggMap = searchResponse.getAggregations().asMap();

            aggMap.forEach((k, v) -> {
                ParsedValueCount teamAgg = (ParsedValueCount) aggMap.get("countcreateTime");
                log.info("数量为{}", teamAgg.getValue());
                log.info(JSONObject.toJSONString(teamAgg));
            });

        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
    }


    /**
     * 搜索 按精准条件搜索 和mysql的 = 一样
     * 布尔查询由一个或多个布尔子句组成，主要包含如下4个：
     * must：    #条件必须满足，相当于and，对于一个文档，所有的查询都必须为真，这个文档才能够匹配成功
     * should:   #相当于or，对于一个文档，查询列表中，只要有一个查询匹配，那么这个文档就被看成是匹配的
     * must_not: #相当于not，对于一个文档，查询列表中的的所有查询都必须都不为真，这个文档才被认为是匹配的
     * filter：  #只过滤符合条件的文档，不计算相关性得分
     * <p>
     * filter:es针对filter会有智能缓存，因此其执行效率很高，做简单匹配查询且不考虑算分时，推荐使用filter代替query
     * <p>
     * should:使用分两种情况：
     * <p>
     * bool查询中包含should,不包含must查询（文档必须满足至少一个条件，minimum_should_match可以控制满足条件的个数或百分比）
     * bool查询中同时包含should和must查询（文档不必满足should中的条件，但是如果满足条件，会增加相关性得分）
     *
     * @param index
     * @param type
     * @param deviceNo
     * @throws IOException
     */
    public void search(String index, String type, String deviceNo) {
        try {
            BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
            // 这里可以根据字段进行搜索，must表示符合条件的，相反的mustnot表示不符合条件的
            boolBuilder.must(QueryBuilders.matchQuery("deviceNo", "0869858030023379"));
            //大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求体的所有内容提供setter。
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(boolBuilder);
            sourceBuilder.from(0);
            sourceBuilder.size(100); // 获取记录数，默认10
            // 第一个是获取字段，第二个是过滤的字段，默认获取全部
            sourceBuilder.fetchSource(new String[]{"id", "deviceNo", "collectTime"}, new String[]{});
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.types(type);
            searchRequest.source(sourceBuilder);
            sourceBuilder.sort(new FieldSortBuilder("createTime").order(SortOrder.DESC));
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            log.info("总数为：{}", hits.getTotalHits());
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit hit : searchHits) {
                log.info("search -> " + hit.getSourceAsString());
            }
        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
    }

    /**
     * 滚动API可用于从搜索请求检索大量结果
     *//*
    public void searchScroll() {
        try {
            SearchRequest searchRequest = new SearchRequest("posts");
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchQuery("title", "Elasticsearch"));
            searchSourceBuilder.size(size);
            searchRequest.source(searchSourceBuilder);
            //设置滚动间隔
            searchRequest.scroll(TimeValue.timeValueMinutes(1L));

            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            //读取返回的滚动id，它指向保持活动的搜索上下文，在下面的搜索滚动调用中需要该上下文
            String scrollId = searchResponse.getScrollId();
            //索第一批搜索结果
            SearchHits hits = searchResponse.getHits();

            //通过设置所需的滚动id和滚动间隔创建SearchScrollRequest
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(TimeValue.timeValueSeconds(30));
            SearchResponse searchScrollResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
            //取新的滚动id，它指向保持活动的搜索上下文，在下面的搜索滚动调用中需要该上下文
            scrollId = searchScrollResponse.getScrollId();
            //检索另一批搜索结果
            hits = searchScrollResponse.getHits();
            assertEquals(3, hits.getTotalHits());
            assertEquals(1, hits.getHits().length);
            assertNotNull(scrollId);

        } catch (IOException e) {
            log.error("滚动API可用于从搜索请求检索大量结果 异常:{}", e.getMessage());
        }
    }*/

    /**
     * 批量操作
     *
     * @throws IOException
     */
    /*public void bulk(List<Tests> testsList) throws IOException {
        // 批量增加
        BulkRequest bulkAddRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Tests tests = testsList.get(i);
            IndexRequest indexRequest = new IndexRequest(INDEX_TEST, TYPE_TEST, tests.getId().toString());
            indexRequest.source(JSON.toJSONString(tests), XContentType.JSON);
            bulkAddRequest.add(indexRequest);
        }
        BulkResponse bulkAddResponse = client.bulk(bulkAddRequest, RequestOptions.DEFAULT);
        System.out.println("bulkAdd: " + JSON.toJSONString(bulkAddResponse));
        search(INDEX_TEST, TYPE_TEST, "this");

        // 批量更新
        BulkRequest bulkUpdateRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Tests tests = testsList.get(i);
            tests.setName(tests.getName() + " updated");
            UpdateRequest updateRequest = new UpdateRequest(INDEX_TEST, TYPE_TEST, tests.getId().toString());
            updateRequest.doc(JSON.toJSONString(tests), XContentType.JSON);
            bulkUpdateRequest.add(updateRequest);
        }
        BulkResponse bulkUpdateResponse = client.bulk(bulkUpdateRequest, RequestOptions.DEFAULT);
        System.out.println("bulkUpdate: " + JSON.toJSONString(bulkUpdateResponse));
        search(INDEX_TEST, TYPE_TEST, "updated");

        // 批量删除
        BulkRequest bulkDeleteRequest = new BulkRequest();
        for (int i = 0; i < testsList.size(); i++) {
            Tests tests = testsList.get(i);
            DeleteRequest deleteRequest = new DeleteRequest(INDEX_TEST, TYPE_TEST, tests.getId().toString());
            bulkDeleteRequest.add(deleteRequest);
        }
        BulkResponse bulkDeleteResponse = client.bulk(bulkDeleteRequest, RequestOptions.DEFAULT);
        System.out.println("bulkDelete: " + JSON.toJSONString(bulkDeleteResponse));
        search(INDEX_TEST, TYPE_TEST, "this");
    }*/
}
